#pragma once

#include "util.hpp"
#include "index.hpp"
#include <iostream>
#include <string>
#include <algorithm>
#include <mutex>
#include <unordered_map>

namespace ns_searcher
{
    class searcher
    {
        using index = ns_index::index;

    public:
        ns_index::index* data_index = nullptr;

    private:
        searcher() = default;
        ~searcher() = default;
        searcher(const searcher&) = delete;
        searcher& operator=(const searcher&) = delete;

    public:
        static searcher* ins;
        static std::mutex mtx;
        static searcher* get_instance()
        {
            if (nullptr == ins)
            {
                mtx.lock();
                if (nullptr == ins)
                    ins = new searcher();
                mtx.unlock();
            }
            return ins;
        }

    public:
        void init_searcher()
        {
            data_index = ns_index::index::get_instance();
            lg(Info, "get index instance success!");
            // data_index->create_index();
            // std::cout << "create all index success\n";
        }

        std::string search(const std::string& query)
        {
            // 1. 分词
            std::vector<std::string> query_words;
            ns_util::jieba_util::get_instance()->cut_for_search(query, &query_words);
            // 2. 根据分词结果倒排索引

            // 这里会出现多个词对应同一份文档的情况，倒排拉链有可能会重复，所以需要去重
            std::unordered_map<uint64_t, index::invertedElem_t> id_map;
            for (const auto& word : query_words)
            {
                const index::inverted_list* wl = data_index->get_inverted_index(word);
                if (nullptr == wl) // 找不到关键词
                    continue;
                // all_word.insert(all_word.end(), wl->begin(), wl->end());
                // 去重
                for (const auto& e : *wl)
                {
                    uint64_t id = e.doc_id;
                    id_map[id].weight += e.weight;
                    id_map[id].doc_id = id;
                    if (id_map[id].key_word.empty())
                        id_map[id].key_word = e.key_word;
                    else if (id_map[id].key_word.size() < e.key_word.size())
                        id_map[id].key_word = e.key_word;
                }
            }
            index::inverted_list all_word;
            for (auto& e : id_map)
                all_word.push_back(std::move(e.second));
            // 3. 对查询结果进行排序
            std::sort(all_word.begin(), all_word.end(), [](const index::invertedElem_t& e1, const index::invertedElem_t& e2)
                { return e1.weight > e2.weight; });
            // 4. 返回json串
            Json::Value root;
            for (const auto& word : all_word)
            {
                // 根据正派索引找到文件内容
                const index::docInfo_t* doc = data_index->get_forward_index(word.doc_id);
                if (nullptr == doc)
                    continue;
                Json::Value elem;
                elem["title"] = doc->title;
                elem["content"] = get_digest(doc->content, word.key_word); // 只需要摘要，不需要全部文本，进行解析
                elem["url"] = doc->url;
                // for debug
                // elem["doc_id"] = doc->doc_id;
                // elem["weight"] = word.weight;
                root.append(elem);
            }
            ns_util::json_util::change_out_sep(""); // 切换快速发送模式
            return ns_util::json_util::serialize(root);
        }

    private:
        std::string get_digest(const std::string& content, const std::string& key)
        {
            // 这里会存在一个问题，由于查找关键字时忽略大小写，但是正排索引中的文件并没有转化，所以需要用search来查找位置
            // size_t pos = content.find(key);
            size_t pos = ns_util::string_util::search_ignore_case(content, key);

            if (std::string::npos == pos)
                return "none";

            const int prev_step = 50;
            const int next_step = 100;
            size_t digest_start = (int)pos - prev_step > 0 ? pos - prev_step : 0;
            // std::cout << pos << std::endl;
            size_t digest_end = pos + next_step;
            std::string digest = "..." + content.substr(digest_start, digest_end - digest_start) + "...\n";
            return digest;
        }
    };
    std::mutex searcher::mtx;
    searcher* searcher::ins;
}